#ifndef __WSOCKET_CPP__
#define __WSOCKET_CPP__

#include "wsocket.h"
#include <netinet/tcp.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <signal.h>
#include <sys/time.h>

static struct epoll_event epev,*cevents;
static int* epfd;
int partconn;
pthread_mutex_t pfreeconnect;
pthread_mutex_t ppush;
int serverstatus, curconn, conncount;

void HandleMessage(struct WServer::MsgHandle::wmsgdata* msg)
{
	int cround = 0;
	if (msg->clientindex > maxconn) {
		delete msg;return;
	}
	switch (msg->type) {
		default:
			cround = clientlist[msg->clientindex].socket_tid;
			servermsglist[cround].push_back(msg);
	}
}

void* socketsend(void* args)
{
	wserPrintMsg("socketsend. pid:%d\n", syscall(SYS_gettid));
	pthread_detach(pthread_self());
	void* data;int clientindex, len;boost::tuple<int, void*, int> bp;
	int ret, inc = (long)args;
	while (1) {
		do {
			bp = PopStream(inc);
			clientindex = bp.get<0>();data = bp.get<1>();len = bp.get<2>();
			if (len && clientlist[clientindex].hclient > 0) {
				ret = send(clientlist[clientindex].hclient, data, len, MSG_DONTWAIT);
				if (ret == -1) {wserPrintErr("send error! ret:%d errno:%d\n", ret, errno);}
				if (*((int*)data+1) != WMSG_SOCKETHIT && *((int*)data+1) != WMSG_SOCKETHITRETURN && *((int*)data+1) != WMSG_SOCKETHITAGAIN && GData::socketslog == 1) {wserPrintMsg("S.%d %d %d\n", clientindex, len, *((int*)data+1));}
			}
		} while (data != NULL && len > 0);
		usleep(10);
	}
	return NULL;
}

void setKeepalive(int socket)
{
	int keepAlive = 1;
	int keepIdle = 240;
	int keepInterval = 2;
	int keepCount = 3;
	if (setsockopt(socket,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == -1) {
		wserPrintErr("error:setsockopt SO_KEEPALIVE \n");
	}
	if (setsockopt(socket,SOL_TCP,TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle)) == -1) {
		wserPrintErr("error:setsockopt TCP_KEEPIDLE \n");
	}
	if (setsockopt(socket,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval)) == -1) {
		wserPrintErr("error:setsockopt TCP_KEEPINTVL \n");
	}
	if (setsockopt(socket,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount)) == -1) {
		wserPrintErr("error:setsockopt TCP_KEEPCNT \n");
	}
}

int getfreeconn(int hclient)
{
	pthread_mutex_lock(&pfreeconnect);
	for (int i = curconn;i < maxconn;i++) {
		if (clientlist[i].hclient == -1) {
			clientlist[i].hclient = hclient;
			pthread_mutex_unlock(&pfreeconnect);
			curconn = i+1;
			return i;
		}
	}
	for (int i = 0;i < curconn;i++) {
		if (clientlist[i].hclient == -1) {
			clientlist[i].hclient = hclient;
			pthread_mutex_unlock(&pfreeconnect);
			curconn = i+1;
			return i;
		}
	}
	pthread_mutex_unlock(&pfreeconnect);
	return -1;
}

void* socketrecv(void* arg)
{
	wserPrintMsg("socketrecv. pid:%d\n", syscall(SYS_gettid));
	pthread_detach(pthread_self());
	int i, hserver, hclient, ret = EXIT_SUCCESS;
	char databuf[MEMORYPOOLSIZE];
	int pthc = 0, inc = (long)arg;
	socklen_t sin_size;
	struct sockaddr_in server_addr, client_addr;

	int on = 1;

	//read flash policy file
	char* Ppolicyfile = 0;
	int policyFsize = 0;
	FILE *ppfp = fopen("crossdomain.file","rb");
	if (ppfp == NULL)
		printf("con't read crossdomain.file!\n");
	else {
		fseek(ppfp,0,SEEK_END);
		int fsize = ftell(ppfp);
		policyFsize = fsize+1;
		Ppolicyfile = new char[fsize+1];
		memset(Ppolicyfile,0,fsize+1);
		fseek(ppfp,0,SEEK_SET);
		int rsize = fread(Ppolicyfile,1,fsize,ppfp);
		fclose(ppfp);
	}
	hserver = socket(AF_INET, SOCK_STREAM, 0);
	if (hserver == -1) return 0;
	wserPrintMsg("Create socket.\n");
	ret = setsockopt(hserver, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	int optVal;socklen_t optLen = sizeof(int);

	optVal = 1024*1024*4;ret = setsockopt(hserver, SOL_SOCKET, SO_RCVBUF, (char*)&optVal, optLen );
	optVal = 1024*1024*4;ret = setsockopt(hserver, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, optLen );
	ret = setsockopt(hserver, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on) );
	setKeepalive(hserver);
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(serverport+inc);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	bzero(&(server_addr.sin_zero), 8);
	wserPrintMsg("Binding to server... IP: %s Port %d \n", INADDR_ANY, serverport+inc);
	if (bind(hserver, (struct sockaddr*)&server_addr, sizeof(sockaddr)) == -1) return 0;
	wserPrintMsg("Bind completed!\n");
	wserPrintMsg("Listening server...\n");
	if (listen(hserver, partconn) == -1) return 0;
	wserPrintMsg("Listen completed!\n");

	epfd[inc] = epoll_create(partconn);
	if (epfd[inc] == -1) return 0;

	memset(&epev, 0, sizeof(struct epoll_event));
	epev.events = EPOLLIN;//|EPOLLHUP|EPOLLERR;
	epev.data.u32 = -1;
	if (epoll_ctl(epfd[inc], EPOLL_CTL_ADD, hserver, &epev)==-1) {
		wserPrintErr("epoll_ctl error! %s\n",strerror(errno));
		return 0;
	}

	while (serverstatus != 2) {
		int nfds = epoll_wait(epfd[inc], cevents+partconn*inc, partconn, -1);
		if (nfds == -1) {
			if (errno == EINTR) continue;
		}
		for (int i = 0;i < nfds;i++) {
			int clientindex = cevents[inc*partconn+i].data.u32;
			if (clientindex == -1) {
				if (serverstatus != 0) continue;
				sin_size = sizeof(sockaddr_in);
				hclient = accept(hserver, (sockaddr*)&client_addr, &sin_size);
				if (hclient <= 0) continue;
				setKeepalive(hclient);
				//found connection.
				int freeconn = getfreeconn(hclient);
				if (freeconn != -1) {
					epev.data.u32 = freeconn;
					if (epoll_ctl(epfd[inc], EPOLL_CTL_ADD, hclient, &epev) == -1) {
						wserPrintErr("2-epoll_ctl error! %s\n",strerror(errno));
						continue;
					}
					WServer::Env::initSocketData(freeconn);
					clientlist[freeconn].hclient = hclient;
					clientlist[freeconn].ip = client_addr.sin_addr;
					clientlist[freeconn].port = client_addr.sin_port;
					clientlist[freeconn].socket_tid = getCurrentRound();
					conncount++;
					if (GData::socketrlog == 1) {
						wserPrintMsg("Received a connection IP:%s! Port:%d playerIndex:%d serverport:%d conncount:%d socket_tid:%d\n", inet_ntoa(clientlist[freeconn].ip), ntohs(clientlist[freeconn].port), freeconn, serverport+inc, conncount, clientlist[freeconn].socket_tid);
					}
					MsgAcceptConnect msg;
					CreateMessage2WMain(freeconn, msg);
				}
				else {
					wserPrintErr("Max connections... \n");
					shutdown(hclient,SHUT_RDWR);
					close(hclient);
				}
			}
			else {
				if (serverstatus == 2) continue;
				hclient = recv(clientlist[clientindex].hclient, databuf, MEMORYPOOLSIZE, 0);
				if (hclient <= 0) {
					epev.data.u32 = clientindex;
					wserPrintMsg("Connction closed. clientIndex:%d\n", clientindex);
					if (epoll_ctl(epfd[inc], EPOLL_CTL_DEL, clientlist[clientindex].hclient, &epev) == -1) {
						wserPrintErr("2-epoll_ctl error! %s\n",strerror(errno));
						shutdown(clientlist[clientindex].hclient, SHUT_RDWR);
						close(clientlist[clientindex].hclient);
					}
					else {
						MsgCloseConnect msg;
						CreateMessage2WMain(clientindex, msg);
						shutdown(clientlist[clientindex].hclient, SHUT_RDWR);
						close(clientlist[clientindex].hclient);
					}
				}
				else {
					if (strncmp((char*)databuf, "tgw", 3) == 0) {
						continue;
					}
					else if (hclient == 23 && strcmp((char*)databuf, "<policy-file-request/>") == 0) {
						if (Ppolicyfile) {
							size_t ss = send(clientlist[clientindex].hclient, (void*)Ppolicyfile, policyFsize, 0);
							epev.data.u32 = clientindex;
							if (epoll_ctl(epfd[inc], EPOLL_CTL_DEL, clientlist[clientindex].hclient, &epev) == -1) {
								wserPrintErr("3-epoll_ctl error! %s\n",strerror(errno));
							}
							shutdown(clientlist[clientindex].hclient, SHUT_RDWR);
							close(clientlist[clientindex].hclient);
							WServer::Env::initSocketData(clientindex);
							clientlist[clientindex].hclient = -1;
							conncount--;
							if (GData::socketrlog == 1) {
								wserPrintMsg("Request policy file. client: %d conncount:%d\n", clientindex, conncount);
								wserPrintMsg("Connction closed. clientIndex:%d\n", clientindex);
							}
						}
						else {
							wserPrintErr("no policy file..\n");
						}
					}
					else {
						pthread_mutex_lock(&ppush);
						if (*((int*)databuf+1) != WMSG_SOCKETHIT && *((int*)databuf+1) != WMSG_SOCKETHITAGAIN && GData::socketrlog == 1) {
							wserPrintMsg("R.%d %d %d\n", clientindex, hclient, *((int*)databuf+1));
						}
						PushStream(clientindex, databuf, hclient, 2, 0);
						pthread_mutex_unlock(&ppush);
					}
				}
			}
		}
		usleep(10);
	}
	return 0;
}

int main(int argc, char *argv[])
{
	signal(SIGPIPE, SIG_IGN); //忽略send产生的异常信号处理。
	moduleindex = MODULE_WSOCKET;
	serverstatus = 0;
	wserInit(moduleindex);

	pthread_t pid;
	int i, ret = EXIT_SUCCESS;
	pthread_mutex_init(&pfreeconnect, 0);
	pthread_mutex_init(&ppush, 0);
	cevents  = new struct epoll_event[GData::maxconn+1];
	curconn = 1;conncount = 0;
	epfd = new int[GData::socketrecvcount];

	partconn = maxconn/GData::socketrecvcount;
	int* portlist = NULL;
	portlist = new int[GData::socketrecvcount];for (i = 0;i < GData::socketrecvcount;i++) portlist[i] = i;
	for (i = 0;i < GData::socketrecvcount;i++) {
		pthread_create(&pid, 0, socketrecv, (void*)(intptr_t)portlist[i]);
	}
	delete[] portlist;portlist = NULL;
	portlist = new int[GData::socketsendcount];for (i = 0;i < GData::socketsendcount;i++) portlist[i] = i;
	for (i = 0;i < GData::socketsendcount;i++) {
		pthread_create(&pid, 0, socketsend, (void*)(intptr_t)portlist[i]);
	}
	delete[] portlist;portlist = NULL;
	while (true) {
		sleep(60);
		wserPrintMsg("MemoryPool.free:%d\n", MemoryPool::instance()->getFreeCount());
	}
	for (i = 0;i < maxconn;i++) {
		if (clientlist[i].hclient > 0) {
			close(clientlist[i].hclient);
		}
	}
exitpos:
	wserUninit();
//	close(epfd);
	delete[] cevents;
	return ret;
}

#endif //__WSOCKET_CPP__
